# routes.py
from fastapi import APIRouter, HTTPException, Depends
from sqlalchemy.orm import Session
from pydantic import BaseModel
from passlib.context import CryptContext
from models import User, Product, Cart, Order, OrderItem
from database import get_db
from typing import Union
import re
from datetime import datetime, timedelta
from jose import JWTError, jwt
from dotenv import load_dotenv
from typing import Union, Optional  # 包含 Optional
import os
router = APIRouter()

# 加载 .env 文件
load_dotenv()

class CustomException(Exception):
    def __init__(self, code: int, msg: str):
        self.code = code
        self.msg = msg

class ResponseModel(BaseModel):
    code: int
    msg: str
    data: Union[dict, list, None]

class UserCreate(BaseModel):
    username: str
    password: str
    email: str

class ProductCreate(BaseModel):
    name: str
    price: float
    stock: int
    description: str = None

class CartItem(BaseModel):
    user_id: int
    product_id: int
    quantity: int

class OrderCreate(BaseModel):
    user_id: int
    cart_items: list[CartItem]


# JWT 配置
SECRET_KEY = os.getenv("SECRET_KEY", "fallback_secret_key")
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", 30))

# 加密器
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")


# 生成访问令牌
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
    """
    生成访问令牌 (JWT)
    :param data: 包含令牌的负载信息
    :param expires_delta: 令牌的过期时间间隔
    :return: 生成的 JWT 令牌字符串
    """
    to_encode = data.copy()
    expire = datetime.utcnow() + (expires_delta if expires_delta else timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt
# 用户名校验：
# 长度限制：用户名长度一般在 6 到 20 个字符之间。
# 允许字符：可以包含字母、数字、下划线，但不能以数字开头。
# 不允许使用敏感词。

# 密码校验：
# 长度限制：密码长度一般在 8 到 20 个字符之间。
# 必须包含至少一个数字、一个字母和一个特殊字符（如 @、#、$、% 等）。
# 不建议使用容易被猜测的密码，比如连续字符或者简单的个人信息。

# 邮箱校验：
# 使用正则表达式来验证邮箱格式。

# 用户名校验
def validate_username(username: str) -> None:
    if len(username) < 6 or len(username) > 20:
        raise CustomException(code=400, msg="用户名长度必须在 6 到 20 个字符之间。")
    if not re.match("^[a-zA-Z_][a-zA-Z0-9_]*$", username):
        raise CustomException(code=400, msg="用户名只能包含字母、数字和下划线，且不能以数字开头。")

# 密码校验
def validate_password(password: str) -> None:
    if len(password) < 8 or len(password) > 20:
        raise CustomException(code=400, msg="密码长度必须在 8 到 20 个字符之间。")
    if not re.search(r"[A-Za-z]", password):
        raise CustomException(code=400, msg="密码必须至少包含一个字母。")
    if not re.search(r"[0-9]", password):
        raise CustomException(code=400, msg="密码必须至少包含一个数字。")
    if not re.search(r"[!@#$%^&*(),.?\":{}|<>]", password):
        raise CustomException(code=400, msg="密码必须至少包含一个特殊字符。")

# 邮箱校验
def validate_email(email: str) -> None:
    email_regex = r"^[\w\.-]+@[\w\.-]+\.\w+$"  # 简单的邮箱正则
    if not re.match(email_regex, email):
        raise CustomException(code=400, msg="无效的电子邮件格式。")
# 初始化密码哈希上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")


@router.post("/register", response_model=ResponseModel)
async def register_user(user: UserCreate, db: Session = Depends(get_db)):
    """
    用户注册接口

    - **username**: 用户名，唯一，不可为空
    - **password**: 密码，不可为空
    - **email**: 电子邮件，唯一，不可为空

    返回：
    - **code**: 状态码，200 表示成功
    - **msg**: 操作信息消息
    - **data**: 额外返回数据，默认为空字典
    """
    try:
        # 验证参数是否为空
        if not user.username:
            raise CustomException(code=400, msg="用户名不能为空。")
        if not user.email:
            raise CustomException(code=400, msg="电子邮件不能为空。")
        if not user.password:
            raise CustomException(code=400, msg="密码不能为空。")

        # 校验用户名、密码和电子邮件
        validate_username(user.username)
        validate_password(user.password)
        validate_email(user.email)

        # 检查用户名或电子邮件是否已存在
        existing_user = db.query(User).filter(
            (User.username == user.username) | (User.email == user.email)
        ).first()
        if existing_user:
            if existing_user.username == user.username:
                raise CustomException(code=400, msg="用户名已存在。")
            if existing_user.email == user.email:
                raise CustomException(code=400, msg="电子邮件已被注册。")

        # 创建新用户实例并设置密码
        hashed_password = pwd_context.hash(user.password)
        new_user = User(username=user.username, email=user.email, password=hashed_password)

        # 保存到数据库
        db.add(new_user)
        db.commit()
        db.refresh(new_user)  # 刷新以获取新用户 ID 等信息

        return ResponseModel(code=200, msg="用户创建成功", data={"id": new_user.id})

    except CustomException as e:
        raise e  # 直接抛出自定义异常
    except Exception as e:
        db.rollback()  # 回滚事务
        raise CustomException(code=500, msg=f"服务器内部错误: {str(e)}")



@router.post("/api/users/login", response_model=ResponseModel)
async def login_user(user: UserCreate, db: Session = Depends(get_db)):
    """
    用户登录接口

    - **username**: 用户名，不可为空
    - **password**: 密码，不可为空

    返回：
    - **code**: 状态码，200 表示成功
    - **msg**: 操作信息消息
    - **data**: 包含用户信息和访问令牌
    """
    try:
        # 验证输入
        if not user.username or not user.password:
            raise CustomException(code=400, msg="用户名或密码不能为空。")

        # 查找用户
        existing_user = db.query(User).filter(User.username == user.username).first()
        if not existing_user:
            raise CustomException(code=400, msg="用户名或密码错误。")

        # 验证密码
        if not pwd_context.verify(user.password, existing_user.password):
            raise CustomException(code=400, msg="用户名或密码错误。")

        # 生成访问令牌
        access_token = create_access_token(
            data={"sub": existing_user.username, "user_id": existing_user.id}
        )

        # 返回成功响应
        return ResponseModel(
            code=200,
            msg="登录成功",
            data={
                "user": {
                    "id": existing_user.id,
                    "username": existing_user.username,
                },
                "token": access_token
            }
        )

    except CustomException as e:
        # 捕获自定义异常
        return ResponseModel(code=e.code, msg=e.msg, data={})
    except Exception as e:
        # 捕获未知错误
        raise CustomException(code=500, msg=f"服务器内部错误: {str(e)}")


@router.post("/api/addProduct", response_model=ResponseModel)
async def add_product(product: ProductCreate, db: Session = Depends(get_db)):
    """
    添加新产品接口

    - **name**: 产品名称，不可为空
    - **price**: 产品价格，不可为空，必须大于零
    - **stock**: 产品库存，不可为空，必须大于或等于零
    - **description**: 产品描述，可选

    返回：
    - **code**: 状态码，200 表示成功，400 表示请求错误，500 表示服务器错误
    - **msg**: 操作信息消息
    - **data**: 包含产品信息，如 id、名称、价格、库存、描述、创建时间、更新信息等
    """
    try:
        # 验证输入
        if not product.name:
            raise CustomException(code=400, msg="产品名称不能为空。")
        if product.price <= 0:
            raise CustomException(code=400, msg="产品价格必须大于零。")
        if product.stock < 0:
            raise CustomException(code=400, msg="产品库存不能为负数。")

        # 检查是否有重复的产品名称
        existing_product = db.query(Product).filter(Product.name == product.name).first()
        if existing_product:
            raise CustomException(code=400, msg="产品名称已存在。")

        # 创建新产品实例
        new_product = Product(
            name=product.name,
            price=product.price,
            stock=product.stock,
            description=product.description,
        )

        # 保存到数据库
        db.add(new_product)
        db.commit()
        db.refresh(new_product)  # 刷新以获取新产品的 ID 等信息

        # 返回成功响应
        return ResponseModel(
            code=200,
            msg="产品添加成功",
            data={
                "id": new_product.id,
                "name": new_product.name,
                "price": new_product.price,
                "stock": new_product.stock,
                "description": new_product.description,
                "created_at": new_product.created_at,  # 假设有创建时间字段
                "updated_at": new_product.updated_at   # 假设有更新时间字段
            }
        )

    except CustomException as e:
        # 捕获自定义异常
        return ResponseModel(code=e.code, msg=e.msg, data={})
    except Exception as e:
        # 捕获未知错误
        db.rollback()  # 回滚事务
        raise CustomException(code=500, msg=f"服务器内部错误: {str(e)}")

@router.get("/api/products", response_model=ResponseModel)
async def get_products(
    limit: int = 10, offset: int = 0, db: Session = Depends(get_db)
):
    """
    获取所有产品接口，支持分页

    参数：
    - **limit**: 每页返回的产品数量，默认为 10
    - **offset**: 偏移量，默认为 0

    返回：
    - **code**: 状态码，200 表示成功
    - **msg**: 操作信息消息
    - **data**: 产品列表，每个产品包含 id、name、price、description 和 stock
    """
    try:
        products = db.query(Product).offset(offset).limit(limit).all()
        product_list = [
            {
                "id": product.id,
                "name": product.name,
                "price": product.price,
                "description": product.description,
                "stock": product.stock,
                "created_at": product.created_at,
                "updated_at": product.updated_at
            }
            for product in products
        ]

        return ResponseModel(code=200, msg="获取成功", data=product_list)
    except Exception as e:
        raise CustomException(code=500, msg=f"服务器内部错误: {str(e)}")

# 关键点说明：
# 输入验证：检查 quantity 是否大于零，同时确认用户和产品是否存在。
# 更新购物车逻辑：当用户向购物车添加已存在的产品时，更新数量而不是重复添加。
# 详细的返回数据：返回购物车项的ID、用户ID、产品ID和数量，帮助前端进行进一步的处理。
# 异常处理：捕获各种异常并返回相应的错误信息。

@router.post("/api/addToCart", response_model=ResponseModel)
async def add_to_cart(item: CartItem, db: Session = Depends(get_db)):
    """
    将产品添加到购物车接口

    - **user_id**: 用户ID，不可为空
    - **product_id**: 产品ID，不可为空
    - **quantity**: 数量，不可为空，必须大于零

    返回：
    - **code**: 状态码，200 表示成功
    - **msg**: 操作信息消息
    - **data**: 包含购物车项的ID、用户ID、产品信息和数量
    """
    try:
        # 验证输入
        if item.quantity <= 0:
            raise CustomException(code=400, msg="数量必须大于零。")

        # 检查是否存在该用户
        user = db.query(User).filter(User.id == item.user_id).first()
        if not user:
            raise CustomException(code=404, msg="用户不存在。")

        # 检查产品是否存在
        product = db.query(Product).filter(Product.id == item.product_id).first()
        if not product:
            raise CustomException(code=404, msg="产品不存在。")

        # 检查购物车中是否已有该产品
        cart_item = db.query(Cart).filter(
            Cart.user_id == item.user_id,
            Cart.product_id == item.product_id
        ).first()

        if cart_item:
            # 如果购物车中已有该产品，更新数量
            cart_item.quantity += item.quantity
        else:
            # 否则，新建购物车项
            cart_item = Cart(
                user_id=item.user_id,
                product_id=item.product_id,
                quantity=item.quantity
            )
            db.add(cart_item)

        db.commit()  # 提交更改
        db.refresh(cart_item)  # 刷新以获取最新信息

        # 返回成功响应
        return ResponseModel(
            code=200,
            msg="产品已成功添加到购物车",
            data={
                "cart_item_id": cart_item.id,
                "user_id": cart_item.user_id,
                "product_id": cart_item.product_id,
                "quantity": cart_item.quantity
            }
        )

    except CustomException as e:
        # 捕获自定义异常
        return ResponseModel(code=e.code, msg=e.msg, data={})
    except Exception as e:
        # 捕获未知错误
        db.rollback()  # 回滚事务
        raise CustomException(code=500, msg=f"服务器内部错误: {str(e)}")


@router.get("/api/cart", response_model=ResponseModel)
async def get_cart(user_id: int, db: Session = Depends(get_db)):
    """
    获取用户购物车中的产品接口

    - **user_id**: 用户ID，不可为空

    返回：
    - **code**: 状态码，200 表示成功
    - **msg**: 操作信息消息
    - **data**: 购物车中的产品列表，每个产品包含 id、name、price、quantity、description 和 total_price 等信息
    """
    try:
        # 获取用户的购物车项
        cart_items = db.query(Cart).filter(Cart.user_id == user_id).all()

        # 如果购物车为空，返回相应消息
        if not cart_items:
            return ResponseModel(code=200, msg="购物车为空", data=[])

        # 获取产品详细信息
        product_list = []
        for item in cart_items:
            product = db.query(Product).filter(Product.id == item.product_id).first()
            if product:  # 确保产品存在
                product_info = {
                    "id": product.id,
                    "name": product.name,
                    "price": product.price,
                    "quantity": item.quantity,
                    "description": product.description,
                    "total_price": product.price * item.quantity,  # 计算总价
                }
                product_list.append(product_info)

        return ResponseModel(
            code=200,
            msg="获取购物车成功",
            data=product_list
        )

    except Exception as e:
        # 捕获未知错误
        return ResponseModel(code=500, msg=f"服务器内部错误: {str(e)}", data={})

# 关键点说明：
# 用户验证：首先检查用户是否存在。
# 订单的总金额和订单项：使用循环遍历 cart_items，检查每个产品的有效性，更新库存，并计算订单的总金额。
# 订单项的创建：在创建 OrderItem 对象时，指定 product_id、quantity 和 price，然后将其存储在列表中。
# 创建订单：创建 Order 对象并关联用户ID、总金额和状态等信息。
# 提交数据：将订单和订单项一起保存到数据库中，保证数据一致性。
# 返回订单信息：成功创建订单后，返回订单ID和相关信息，方便前端显示。
@router.post("/api/createOrders", response_model=ResponseModel)
async def create_order(order: OrderCreate, db: Session = Depends(get_db)):
    """
    创建订单接口

    - **user_id**: 用户ID，不可为空
    - **cart_items**: 购物车项目列表，包含每个项目的 product_id 和数量

    返回：
    - **code**: 状态码，200 表示成功
    - **msg**: 操作信息消息
    - **data**: 订单详情，包括订单ID等信息
    """
    try:
        # 验证用户
        user = db.query(User).filter(User.id == order.user_id).first()
        if not user:
            raise CustomException(code=404, msg="用户不存在。")

        total_amount = 0  # 初始化总金额
        order_items = []  # 用于存储订单项

        # 遍历购物车项目
        for item in order.cart_items:
            # 查找对应的产品
            product = db.query(Product).filter(Product.id == item.product_id).first()
            if not product:
                raise CustomException(code=404, msg=f"产品ID {item.product_id} 不存在。")
            if item.quantity <= 0:
                raise CustomException(code=400, msg="数量必须大于零。")
            if item.quantity > product.stock:
                raise CustomException(code=400, msg=f"产品 {product.name} 库存不足。")

            # 更新库存
            product.stock -= item.quantity

            # 计算总金额
            total_amount += product.price * item.quantity

            # 创建订单项对象
            order_item = OrderItem(
                product_id=product.id,
                quantity=item.quantity,
                price=product.price
            )
            order_items.append(order_item)  # 将订单项添加到列表中

        # 创建订单对象
        new_order = Order(
            user_id=order.user_id,
            total_amount=total_amount,
            status="待支付",  # 订单状态可以设定为"待支付"或其他状态
            created_at=datetime.utcnow()  # 记录订单创建时间
        )

        db.add(new_order)  # 添加订单
        db.commit()  # 提交更改，以生成订单ID
        db.refresh(new_order)  # 刷新以获取新订单的 ID

        # 将订单项与订单关联
        for item in order_items:
            item.order_id = new_order.id  # 设置订单项的order_id
            db.add(item)

        db.commit()  # 提交订单项

        # 返回成功响应
        return ResponseModel(
            code=200,
            msg="订单创建成功",
            data={
                "order_id": new_order.id,
                "total_amount": new_order.total_amount,
                "status": new_order.status,
                "order_items": [
                    {
                        "product_id": item.product_id,
                        "quantity": item.quantity,
                        "price": item.price
                    } for item in order_items
                ]
            }
        )

    except CustomException as e:
        # 捕获自定义异常
        return ResponseModel(code=e.code, msg=e.msg, data={})
    except Exception as e:
        # 捕获未知错误
        db.rollback()  # 回滚事务
        return ResponseModel(code=500, msg=f"服务器内部错误: {str(e)}", data={})

# 关键点说明：
# 关键词搜索：使用 SQLAlchemy 的 like 方法进行模糊搜索，能够匹配产品名称中包含指定关键字的所有产品。
# 详细的返回信息：返回的产品信息更加详细，包括 ID、名称、价格、库存、描述以及创建时间等。
# 处理未找到产品的情况：如果没有找到符合条件的产品，返回提示消息，而不是仅仅返回空列表，提升用户体验。
# 异常处理：确保接口在发生错误时也能返回适当的错误信息。
@router.get("/api/searchProducts", response_model=ResponseModel)
async def search_products(query: str, db: Session = Depends(get_db)):
    """
    搜索产品接口

    - **query**: 搜索关键字，产品名称中的一部分

    返回：
    - **code**: 状态码，200 表示成功
    - **msg**: 操作信息消息
    - **data**: 符合条件的产品列表，每个产品包含 ID、name、price、stock、description 和其他信息
    """
    try:
        # 搜索产品
        products = db.query(Product).filter(Product.name.like(f"%{query}%")).all()
        print('所有产品',products)

        # 检查是否找到产品
        if not products:
            return ResponseModel(code=200, msg="未找到符合条件的产品", data=[])

        # 构造返回的产品列表
        product_list = [
            {
                "id": p.id,
                "name": p.name,
                "price": p.price,
                "stock": p.stock,
                "description": p.description,
                "created_at": p.created_at,  # 假设产品有创建时间
            }
            for p in products
        ]

        return ResponseModel(
            code=200,
            msg=f"共找到 {len(product_list)} 个产品",
            data=product_list
        )

    except Exception as e:
        # 捕获未知错误
        return ResponseModel(code=500, msg=f"服务器内部错误: {str(e)}", data={})
